/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package fr.ove.openmath.mfd2.omparser;

/**
 *
 * @author Mr.Sam
 */
import java.io.*;
import java.util.*;
import fr.inria.openmath.omapi.*;
import fr.ove.openmath.exceptions.*;

/**
* A handler for the OpenMath parser of the Mfd2 application.
*
*/
public class Mfd2ParserHandler extends ParserAdapter {
    private Stack stackIndentation = new Stack();
    private String omObjectParsed = new String();
    private String indentation = "";

    private boolean isError = false;
    private String errorObjects = new String();
    private Symbol errorSymbol;

    private ErrorMessageException eme = null;

    /**
    * Returns the OpenMath object parsed.
    */
    public String getOmObjectParsed() {
        return omObjectParsed;
    }

    /**
    * Throws an OpenMath exception.
    * @param message the message to display.
    * @param e a specific exception to throw.
    * @see fr.inria.openmath.omapi.OMException
    */
    private void throwOME(String message, Exception e) throws OMException {
        throw new OMException(message, e);
    }

    /**
    * Throws an OpenMath exception.
    * @param message the message to display.
    * @param code the code of the excpetion thrown.
    * @see fr.inria.openmath.omapi.OMException
    */
    private void throwOME(String message, short code) throws OMException {
        throw new OMException(message, code);
    }

    /**
    * Throws an OpenMath exception.
    * @param message the message to display.
    * @see fr.inria.openmath.omapi.OMException
    */
    private void throwOME(String message) throws OMException {
        throwOME(message, OMException.OME_GENERIC);
    }


    /*******************************************/
    /*                                         */
    /* Les méthodes de la classe ParserAdapter */
    /*                                         */
    /*******************************************/

    /**
     * Receives notification of the beginning of a object element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #endObject
     */
    public void startObject() throws OMException {
        stackIndentation.removeAllElements();
        omObjectParsed = "";
        indentation = "";
        isError = false;
        errorObjects = "";
        errorSymbol = null;
        eme = null;

        omObjectParsed += indentation + "<OMOBJ>\n";
        stackIndentation.push(indentation);
        indentation += "  ";
    }

    /**
     * Receives notification of the end of an object element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #startObject
     */
    public void endObject() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMOBJ>";

        // Si le test est vrai, alors c'est qu'on a filtré un symbol pour lever cette exception
        // On ne devrait pas avoir (eme != null) et isError = true en même temps.
        if (eme != null)
            throw new OMException(eme);
        else if (isError)
            throwErrorMessageException();
    }

    /**
     * Receives notification of the beginning of a application element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #endApplication
     */
    public void startApplication() throws OMException {
        omObjectParsed += indentation + "<OMA>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMA>\n";

        indentation += "  ";
    }

    /**
     * Receives notification of the end of an application element.
     *
     * <p>By default, do nothing. Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #startApplication
     */
    public void endApplication() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMA>\n";

        if (isError)
            errorObjects += indentation + "</OMA>\n";
    }


    /**
     * Receives notification of the beginning of a attribution element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #endAttribution
     */
    public void startAttribution() throws OMException {
        omObjectParsed += indentation + "<OMATTR>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMATTR>\n";

        indentation += "  ";
    }

    /**
     * Receives notification of the end of an attribution element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #startAttribution
     */
    public void endAttribution() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMATTR>\n";

        if (isError)
            errorObjects += indentation + "</OMATTR>\n";
    }

    /**
     * Receives notification of the beginning of an attribute-pairs element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #endAttributePairs
     */
    public void startAttributePairs() throws OMException {
        omObjectParsed += indentation + "<OMATP>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMATP>\n";

        indentation += "  ";
    }

    /**
     * Receives notification of the end of an attribute-pairs element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #startAttribution
     */
    public void endAttributePairs() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMATP>\n";

        if (isError)
            errorObjects += indentation + "</OMATP>\n";
    }

    /**
     * Receives notification of the beginning of a Bind element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #endBind
     */
    public void startBind() throws OMException {
        omObjectParsed += indentation + "<OMBIND>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMBIND>\n";

        indentation += "  ";
    }

    /**
     * Receives notification of the beginning of a BVars element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #endBVars
     */
    public void startBVars() throws OMException {
        omObjectParsed += indentation + "<OMBVAR>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMBVAR>\n";

        indentation += "  ";
    }

    /**
     * Receives notification of the beginning of a BVars element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #startBVars
     */
    public void endBVars() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMBVAR>\n";

        if (isError)
            errorObjects += indentation + "</OMBVAR>\n";
    }


    /**
     * Receives notification of the beginning of a Bind element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #endBind
     */
    public void endBind() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OMBIND>\n";

        if (isError)
            errorObjects += indentation + "</OMBIND>\n";
    }

    /**
     * Receives notification of the beginning of a error element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     * @see #endError
     */
    public void startError() throws OMException {
        omObjectParsed += indentation + "<OME>\n";
        stackIndentation.push(indentation);

        if (isError)
            errorObjects += indentation + "<OMATTR>\n";
        else
            isError = true;

        indentation += "  ";
    }

    /**
     * Receives notification of the end of an error element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see #startError
     */
    public void endError() throws OMException {
        indentation = (String) stackIndentation.pop();

        omObjectParsed += indentation + "</OME>\n";

        if (isError)
            errorObjects += indentation + "</OME>\n";
    }


    /**
     * Receives notification of an Integer element.
     *
     * <p>By default, do nothing. Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this Integer element. This is an infinite precision integer.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see fr.inria.openmath.omapi.BigInt
     */
    public void readInteger(BigInt value) throws OMException {
        String strValue = (value.getSign() == 1) ? value.getDigits() : "-" + value.getDigits();
        
        omObjectParsed += indentation + "<OMI> " + strValue + " </OMI>\n";

        if (isError)
            errorObjects += indentation + "<OMI> " + strValue + " </OMI>\n";
    }

    /**
     * Receives notification of a floating-point number element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this Float element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     */
    public void readFloat(double value) throws OMException {
        omObjectParsed += indentation + "<OMF dec = \"" + value + " \"/>\n";

        if (isError)
            errorObjects += indentation + "<OMF dec = \"" + value + " \"/>\n";
    }

    /**
     * Receives notification of a string element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this String element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     */
    public void readString(String value) throws OMException {
        omObjectParsed += indentation + "<OMSTR> " + value + " </OMSTR>\n";

        if (isError)
            errorObjects += indentation + "<OMSTR> " + value + " </OMSTR>\n";
    }

    /**
     * Receives notification of a variable element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param name the name of this Variable element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     */
    public void readVariable(String name) throws OMException {
        omObjectParsed += indentation + "<OMV name=\""+ name +"\"/> \n";

        if (isError)
            errorObjects += indentation + "<OMV name=\""+ name +"\"/> \n";
    }

    /**
     * Receives notification of a byteArray element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this ByteArray element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     *
     */
    public void readByteArray(byte[] value) throws OMException {
    }

    /**
     * Receives notification of a symbol element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this Symbol element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see Symbol
     */
    public void readSymbol(Symbol value) throws OMException {
        byte errorCode;

        omObjectParsed += indentation + "<OMS cd=\""+value.getCD()+"\" name=\""+value.getName()+"\"/> \n";

        if (isError) {
            if (errorSymbol == null)
                errorSymbol = value;
            else
                errorObjects += indentation + "<OMS cd=\""+value.getCD()+"\" name=\""+value.getName()+"\"/> \n";

            return;
        }
        else
            filterSymbol(value);
    }

    /**
     * Receives notification of a comment element.
     *
     * <p>By default, do nothing.  Application writers may override this
     * method in a subclass.</p>
     *
     * @param value the value embeded in this Comment element.
     * @exception fr.inria.openmath.omapi.OMException Any OpenMath exception.
     * @see Comment
     */
    public void readComment(String value) throws OMException {}

    private void filterSymbol(Symbol value) throws OMException {
        // le cd mfd2
        if (value.getCD().equals("mfd2")) {
            if (value.getName().equals("dontknow"))
                eme = new Mfd2Exception("dontknow");
            else if (value.getName().equals("wrongRequest"))
                eme = new Mfd2Exception("wrongRequest");
            return;
        }

        // le cd control
        if (value.getCD().equals("control")) {
            eme = new ControlException(value.getName());
            return;
        }

    }

    private void throwErrorMessageException() throws OMException {
        if (errorSymbol.getCD().equals("mfd2")) {
            // Normalement aucun ????
            throw new OMException(new Mfd2Exception(errorSymbol.getName()));
        }
        else if (errorSymbol.getCD().equals("control"))
            throw new OMException(new ControlException(errorSymbol.getName(), errorObjects));
    }

    /**
    * error must be : unhandledSymbol, or unknownCD or unknownSymbol
    */
    private void throwControlException(Symbol symbol, String error) throws OMException {
        ControlException controlException = new ControlException(error, symbol);
        throw new OMException(controlException);
    }
}
